#pragma once

#include <iostream>
#include <algorithm>
#include <memory>
#include <cstring>
#include <sys/epoll.h>
#include "Socket.hpp"

using namespace SocketModule;
class epollservice
{
    const static int defaultFd = -1;
    const static int size = 64;

public:
    epollservice(uint16_t port)
        : _listensock(std::make_unique<TcpSocket>()),
          _isRunning(false),
          _epfd(defaultFd)
    {
        _listensock->BuildTcpSocket(port);
        // 1.创建epoll模型
        _epfd = epoll_create(1);
        if (_epfd < 0)
        {
            LOG(loglevel::FATAL) << "create epoll error";
            exit(CREATE_EPOLL_ERR);
        }
        LOG(loglevel::DEBUG) << "create epoll success";

        // 2.关心listensockfd
        struct epoll_event ev;
        ev.data.fd = _listensock->Fd();
        ev.events = EPOLLIN;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensock->Fd(), &ev);
        if (n < 0)
        {
            LOG(loglevel::FATAL) << "epoll_ctl add listensockfd error";
            exit(EPOLL_CTL_ADD_ERR);
        }
        LOG(loglevel::DEBUG) << "epoll_ctl add listensockfd success";
    }

    ~epollservice()
    {
        _listensock->Close();
        if (_epfd > 0)
        {
            close(_epfd);
        }
    }

    void Strat()
    {
        _isRunning = true;
        int timeout = -1; // ms
        while (_isRunning)
        {
            int n = epoll_wait(_epfd, _revs, size, timeout);
            switch (n)
            {
            case 0: // 超时
                LOG(loglevel::INFO) << "timeout...";
                break;
            case -1:
                LOG(loglevel::FATAL) << "epoll_wait error";
                break;
            default:
                Dispath(n);
                break;
            }
        }
        _isRunning = false;
    }

    // 事件派发器
    void Dispath(int rnum)
    {
        for(int i=0; i<rnum; i++) {
            epoll_event rev = _revs[i];
            int fd = _revs[i].data.fd;

            if(rev.events & EPOLLIN) {
                if(fd == _listensock->Fd()) {
                    Accepter();
                }
                else {
                    Recver(fd);
                }
            }
        }
    }

    // 连接管理器
    void Accepter()
    {
        InetAddr client;
        int sockfd = _listensock->Accept(&client);

        // 将来这个连接也是需要IO的，所以交给epoll去等待
        epoll_event ev;
        ev.data.fd = sockfd;
        ev.events = EPOLLIN;
        int n = epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &ev);
        if(n < 0) {
            LOG(loglevel::WARINING) << "epoll_ctl add sockfd error";
        }
        else {
            LOG(loglevel::INFO) << "epoll_ctl add sockfd success ->"  << sockfd;
        }
    }

    // IO处理器
    void Recver(int fd)
    {
        // 到了IO处理器，说明此时该fd的读事件是就绪的，现在直接recv/read不会阻塞
        // 但是直接读取还是有问题的，因为Tcp报文是面向字节流的，我们不知道是否能将一个报文读全
        char buffer[1024] = {0};
        ssize_t n = recv(fd, buffer, sizeof(buffer) - 1, 0); // 0表示阻塞读取，但这里绝对不会阻塞
        if (n > 0)
        {
            std::cout << "client say$ " << buffer << std::endl;
        }
        else if (n == 0)
        {
            // 客户端退出，将该连接关闭，同时从辅助数组中去除
            std::cout << "client quit" << std::endl;
            int m = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            if(m < 0) {
                LOG(loglevel::WARINING) << "epoll_ctl delete error";
            }
            else {
                LOG(loglevel::INFO) << "epoll_ctl delete sockfd success -> " << fd;
            }
            close(fd);
        }
        else
        {
            int m = epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            if(m < 0) {
                LOG(loglevel::WARINING) << "epoll_ctl delete error";
            }
            else {
                LOG(loglevel::INFO) << "epoll_ctl delete sockfd success -> " << fd;
            }
            close(fd);
        }
    }

private:
    std::unique_ptr<Socket> _listensock;
    bool _isRunning;
    int _epfd;
    struct epoll_event _revs[size];
};